package org.example.dbcompare.service;

import cn.hutool.db.ds.simple.SimpleDataSource;
import cn.hutool.db.meta.Column;
import cn.hutool.db.meta.MetaUtil;
import cn.hutool.db.meta.Table;
import cn.hutool.db.meta.TableType;
import lombok.extern.slf4j.Slf4j;
import org.example.dbcompare.model.ColumnMismatch;
import org.example.dbcompare.model.CompareContext;
import org.example.dbcompare.model.CompareResult;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * 数据库对比服务
 * @author wuyong
 * @date 2025/10/22
 */
@Slf4j
public class CompareService {
    private IgnoreTableConfigService ignoreTableConfigService = new IgnoreTableConfigService();
    /**
     * 线程池，并行度为8
     */
    private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(8, r -> {
        Thread thread = new Thread(r, "db-compare-" + System.currentTimeMillis());
        thread.setDaemon(true);
        return thread;
    });
    
    /**
     * 对比两个数据库的结构差异
     * @param compareContext 对比上下文
     * @return 对比结果
     */
    public CompareResult compare(CompareContext compareContext) {
        log.info("开始数据库结构对比");
        
        CompareResult compareResult = new CompareResult();
        SimpleDataSource sourceDataSource = new SimpleDataSource(
            compareContext.getSourceDb().getUrl(), 
            compareContext.getSourceDb().getUsername(), 
            compareContext.getSourceDb().getPassword()
        );
        SimpleDataSource targetDataSource = new SimpleDataSource(
            compareContext.getTargetDb().getUrl(), 
            compareContext.getTargetDb().getUsername(), 
            compareContext.getTargetDb().getPassword()
        );
        
        // 获取所有表名
        List<String> srcTableNames = MetaUtil.getTables(sourceDataSource);
        List<String> targetTableNames = MetaUtil.getTables(targetDataSource);
        
        // 对比缺失的表
        Set<String> missingTables = srcTableNames.stream()
                .filter(tableName -> !ignoreTableConfigService.shouldIgnoreTable(tableName, compareContext.getIgnoreTableConfig()))
            .filter(tableName -> !targetTableNames.contains(tableName))
            .collect(Collectors.toSet());
        compareResult.setMissingTables(missingTables);
        
        // 对比各表的列结构
        Map<String, Set<String>> missingColumns = new ConcurrentHashMap<>();
        Map<String, Set<ColumnMismatch>> columnTypeMismatches = new ConcurrentHashMap<>();
        
        // 只对比两个数据库都存在的表
        Set<String> commonTables = srcTableNames.stream()
            .filter(targetTableNames::contains)
            .collect(Collectors.toSet());
        Set<String> compareTables = commonTables.stream()
            .filter(tableName -> !ignoreTableConfigService.shouldIgnoreTableStructure(tableName, compareContext.getIgnoreTableConfig()))
            .collect(Collectors.toSet());
        // 收集所有异步任务
        List<CompletableFuture<Void>> futures = new ArrayList<>();
            
        for (String tableName : compareTables) {
            log.info("对比表: {}", tableName);
            
            try {
                Table srcTable = MetaUtil.getTableMeta(sourceDataSource, tableName);
                Table targetTable = MetaUtil.getTableMeta(targetDataSource, tableName);
                
                // 创建异步任务并添加到列表中
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    compareTableColumns(srcTable, targetTable, tableName, missingColumns, columnTypeMismatches);
                }, THREAD_POOL);
                futures.add(future);
                
            } catch (Exception e) {
                log.error("对比表 {} 时发生错误: {}", tableName, e.getMessage(), e);
            }
        }
        
        // 等待所有异步任务完成
        if (!futures.isEmpty()) {
            try {
                CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
                log.info("所有表的列结构对比任务已完成");
            } catch (InterruptedException | ExecutionException e) {
                log.error("等待异步任务完成时发生错误: {}", e.getMessage(), e);
                Thread.currentThread().interrupt(); // 恢复中断状态
            }
        }
        
        compareResult.setMissingColumns(missingColumns);
        compareResult.setColumnTypeMismatches(columnTypeMismatches);
        
        log.info("数据库结构对比完成");
        return compareResult;
    }
    
    /**
     * 对比两个表的列结构
     */
    private void compareTableColumns(Table srcTable, Table targetTable, String tableName,
                                   Map<String, Set<String>> missingColumns,
                                   Map<String, Set<ColumnMismatch>> columnTypeMismatches) {
        
        Map<String, Column> srcColumns = srcTable.getColumns().stream().collect(Collectors.toMap(Column::getName, column -> column));
        Map<String, Column> targetColumns = targetTable.getColumns().stream().collect(Collectors.toMap(Column::getName, column -> column));;
        
        // 找出目标表中缺失的列
        Set<String> missingCols = srcColumns.keySet().stream()
            .filter(columnName -> !targetColumns.containsKey(columnName))
            .collect(Collectors.toSet());
            
        if (!missingCols.isEmpty()) {
            missingColumns.put(tableName, missingCols);
            log.info("表 {} 中缺失的列: {}", tableName, missingCols);
        }
        
        // 对比列类型不匹配的情况
        Set<ColumnMismatch> mismatches = new HashSet<>();
        
        for (String columnName : srcColumns.keySet()) {
            if (targetColumns.containsKey(columnName)) {
                Column srcColumn = srcColumns.get(columnName);
                Column targetColumn = targetColumns.get(columnName);
                
                ColumnMismatch mismatch = compareColumn(srcColumn, targetColumn, columnName);
                if (mismatch != null) {
                    mismatches.add(mismatch);
                }
            }
        }
        
        if (!mismatches.isEmpty()) {
            columnTypeMismatches.put(tableName, mismatches);
            log.info("表 {} 中列类型不匹配: {}", tableName, mismatches.size() + " 个列");
        }
    }
    
    /**
     * 对比单个列的属性
     */
    private ColumnMismatch compareColumn(Column srcColumn, Column targetColumn, String columnName) {
        boolean hasMismatch = false;
        ColumnMismatch.ColumnMismatchBuilder mismatchBuilder = ColumnMismatch.builder()
            .columnName(columnName);
        
        // 对比列类型
        String srcType = srcColumn.getTypeName();
        String targetType = targetColumn.getTypeName();
        if (!Objects.equals(srcType, targetType)) {
            mismatchBuilder.sourceType(srcType).targetType(targetType);
            hasMismatch = true;
        }
        
        // 对比列长度
        long srcLength = srcColumn.getSize();
        long targetLength = targetColumn.getSize();
        if (!Objects.equals(srcLength, targetLength)) {
            mismatchBuilder.sourceLength(srcLength).targetLength(targetLength);
            hasMismatch = true;
        }
        
//        // 对比精度
//        Integer srcPrecision = srcColumn.get();
//        Integer targetPrecision = targetColumn.getPrecision();
//        if (!Objects.equals(srcPrecision, targetPrecision)) {
//            mismatchBuilder.sourcePrecision(srcPrecision).targetPrecision(targetPrecision);
//            hasMismatch = true;
//        }
        
        // 对比小数位数
        Integer srcScale = srcColumn.getDigit();
        Integer targetScale = targetColumn.getDigit();
        if (!Objects.equals(srcScale, targetScale)) {
            mismatchBuilder.sourceScale(srcScale).targetScale(targetScale);
            hasMismatch = true;
        }
        
        // 对比是否允许NULL
        Boolean srcNullable = srcColumn.isNullable();
        Boolean targetNullable = targetColumn.isNullable();
        if (!Objects.equals(srcNullable, targetNullable)) {
            mismatchBuilder.sourceNullable(srcNullable).targetNullable(targetNullable);
            hasMismatch = true;
        }
        
//        // 对比默认值
//        String srcDefault = srcColumn.get;
//        String targetDefault = targetColumn.getDefaultValue();
//        if (!Objects.equals(srcDefault, targetDefault)) {
//            mismatchBuilder.sourceDefaultValue(srcDefault).targetDefaultValue(targetDefault);
//            hasMismatch = true;
//        }
        
        return hasMismatch ? mismatchBuilder.build() : null;
    }
    public boolean getTableMeta(DataSource dataSource, String tableName) {
        try {
            Connection conn = dataSource.getConnection();
            String catalog = conn.getCatalog();
            String schema = conn.getSchema();
            DatabaseMetaData metaData = conn.getMetaData();
            // 检查表是否存在
            try (ResultSet rs = metaData.getTables(catalog, schema, tableName, new String[]{TableType.TABLE.value()})) {
                if (!rs.next()) {
                    log.warn("表 {} 不存在", tableName);
                    return false;
                }
                return true;
            }
        } catch (SQLException e) {
            log.error("检查表 {} 时发生错误: {}", tableName, e.getMessage(), e);
            return false;
        }
    }
}
